{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# 🧠 Persistent Memory Agent + AdalFlow (Colab Tutorial)\n",
        "\n",
        "This Colab walks you through building a **memory-aware AI agent** using **AdalFlow** with:\n",
        "- **Persistent memory** across multiple sessions (file-backed)\n",
        "- **Short-term vs long-term** knowledge\n",
        "- **History compaction** (auto-summarization to avoid prompt bloat)\n",
        "- **Memory tools** (`remember`, `recall`, `jot`, `counter`) the agent can call\n",
        "- **Design principles** for agent knowledge & architecture"
      ],
      "metadata": {
        "id": "8BVYEBB33cLz"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from IPython.display import clear_output\n",
        "\n",
        "\n",
        "!pip install adalflow pydantic openai\n",
        "\n",
        "# Set OpenAI API key (replace with your actual key)\n",
        "import os\n",
        "os.environ[\"OPENAI_API_KEY\"] = \"\"\n",
        "# Optional: Set Anthropic API key for Anthropic examples\n",
        "# os.environ[\"ANTHROPIC_API_KEY\"] = \"your-anthropic-api-key-here\"\n",
        "clear_output()"
      ],
      "metadata": {
        "id": "nFrfHjtv3d8X"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Setup and Imports"
      ],
      "metadata": {
        "id": "_FsTzCDA4V_C"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import json\n",
        "import os\n",
        "import threading\n",
        "from datetime import datetime\n",
        "from typing import Any, Dict, List, Optional\n",
        "\n",
        "from adalflow.components.agent.agent import Agent\n",
        "from adalflow.components.agent.runner import Runner\n",
        "from adalflow.components.model_client import OpenAIClient\n",
        "from adalflow.components.model_client.anthropic_client import AnthropicAPIClient\n",
        "from adalflow.core.func_tool import FunctionTool"
      ],
      "metadata": {
        "id": "N6Lf6LfU38nz",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "4292a767-abc5-4ed8-ec0f-1133ef8a21ce"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "WARNING:adalflow.tracing.mlflow_integration:MLflow not available. Install with: pip install mlflow\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# JSONMemoryStore (persistent, thread-safe)\n",
        "\n",
        "You want agent knowledge to outlive the Python process and be safe under concurrent access.\n",
        "\n",
        "**Key ideas**\n",
        "\n",
        "- short_term: injected verbatim into the prompt (fast recall, higher token cost).\n",
        "\n",
        "- long_term: durable facts + an evolving summary created from older history (compact).\n",
        "\n",
        "- global: rare, cross-session settings (e.g., shared counters, feature flags).\n",
        "\n",
        "**Important methods**\n",
        "\n",
        "- ensure_session(session_id): lazily creates session buckets.\n",
        "\n",
        "- append_history(session_id, role, content): adds turns.\n",
        "\n",
        "- remember_fact(session_id, key, value, scope): writes to short_term or long_term.\n",
        "\n",
        "- recall_fact(session_id, key): reads (prefers short_term, falls back to long_term).\n",
        "\n",
        "- set_summary(session_id, summary): saves a long-term compressed summary.\n",
        "\n",
        "Swap JSON for SQLite/Postgres/Redis later; keep the same method signatures so the rest of the code doesn't change."
      ],
      "metadata": {
        "id": "C0ihKNIs5Dop"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# ========= Persistent Memory Layer =========\n",
        "class JSONMemoryStore:\n",
        "    \"\"\"\n",
        "    Thread-safe, file-backed memory store.\n",
        "    Structure:\n",
        "    {\n",
        "      \"global\": {...},                       # global facts/counters\n",
        "      \"sessions\": {\n",
        "        \"<session_id>\": {\n",
        "          \"created_at\": \"...\",\n",
        "          \"short_term\": { \"notes\": [...], \"facts\": {...} },\n",
        "          \"long_term\": { \"summary\": \"...\", \"facts\": {...} },\n",
        "          \"history\": [ {\"role\":\"user\",\"content\":\"...\"}, {\"role\":\"assistant\",\"content\":\"...\"} ]\n",
        "        }\n",
        "      }\n",
        "    }\n",
        "    \"\"\"\n",
        "    def __init__(self, path: str = \"memory_store.json\"):\n",
        "        self.path = path\n",
        "        self.lock = threading.Lock()\n",
        "        if not os.path.exists(self.path):\n",
        "            with open(self.path, \"w\", encoding=\"utf-8\") as f:\n",
        "                json.dump({\"global\": {}, \"sessions\": {}}, f, ensure_ascii=False, indent=2)\n",
        "        self._cache = self._load()\n",
        "\n",
        "    def _load(self) -> Dict[str, Any]:\n",
        "        with open(self.path, \"r\", encoding=\"utf-8\") as f:\n",
        "            return json.load(f)\n",
        "\n",
        "    def _save(self) -> None:\n",
        "        with open(self.path, \"w\", encoding=\"utf-8\") as f:\n",
        "            json.dump(self._cache, f, ensure_ascii=False, indent=2)\n",
        "\n",
        "    def get(self) -> Dict[str, Any]:\n",
        "        with self.lock:\n",
        "            return self._cache\n",
        "\n",
        "    def upsert_global(self, key: str, value: Any) -> None:\n",
        "        with self.lock:\n",
        "            self._cache.setdefault(\"global\", {})[key] = value\n",
        "            self._save()\n",
        "\n",
        "    def get_global(self, key: str, default: Any = None) -> Any:\n",
        "        with self.lock:\n",
        "            return self._cache.get(\"global\", {}).get(key, default)\n",
        "\n",
        "    def ensure_session(self, session_id: str) -> None:\n",
        "        with self.lock:\n",
        "            if session_id not in self._cache[\"sessions\"]:\n",
        "                self._cache[\"sessions\"][session_id] = {\n",
        "                    \"created_at\": datetime.utcnow().isoformat(),\n",
        "                    \"short_term\": {\"notes\": [], \"facts\": {}},\n",
        "                    \"long_term\": {\"summary\": \"\", \"facts\": {}},\n",
        "                    \"history\": [],\n",
        "                }\n",
        "                self._save()\n",
        "\n",
        "    def append_history(self, session_id: str, role: str, content: str) -> None:\n",
        "        with self.lock:\n",
        "            self._cache[\"sessions\"][session_id][\"history\"].append({\"role\": role, \"content\": content})\n",
        "            self._save()\n",
        "\n",
        "    def add_note(self, session_id: str, note: str) -> None:\n",
        "        with self.lock:\n",
        "            self._cache[\"sessions\"][session_id][\"short_term\"].setdefault(\"notes\", []).append(note)\n",
        "            self._save()\n",
        "\n",
        "    def remember_fact(self, session_id: str, key: str, value: Any, scope: str = \"short\") -> None:\n",
        "        with self.lock:\n",
        "            bucket = \"long_term\" if scope == \"long\" else \"short_term\"\n",
        "            self._cache[\"sessions\"][session_id][bucket].setdefault(\"facts\", {})[key] = value\n",
        "            self._save()\n",
        "\n",
        "    def recall_fact(self, session_id: str, key: str, default: Any = None) -> Any:\n",
        "        with self.lock:\n",
        "            sess = self._cache[\"sessions\"][session_id]\n",
        "            # Prefer short-term, then long-term\n",
        "            return (\n",
        "                sess.get(\"short_term\", {}).get(\"facts\", {}).get(key) or\n",
        "                sess.get(\"long_term\", {}).get(\"facts\", {}).get(key, default)\n",
        "            )\n",
        "\n",
        "    def set_summary(self, session_id: str, summary: str) -> None:\n",
        "        with self.lock:\n",
        "            self._cache[\"sessions\"][session_id][\"long_term\"][\"summary\"] = summary\n",
        "            self._save()\n",
        "\n",
        "    def get_session_blob(self, session_id: str) -> Dict[str, Any]:\n",
        "        with self.lock:\n",
        "            return self._cache[\"sessions\"][session_id]"
      ],
      "metadata": {
        "id": "ExKefKrU4gg1"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# Instantiate the persistent store (one per process)\n",
        "MEMSTORE = JSONMemoryStore(path=\"memory_store.json\")"
      ],
      "metadata": {
        "id": "-KIiCSCl5vVE"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Taming prompt bloat: HistoryCompactor\n",
        "\n",
        "Design: When history.length > threshold (e.g., 18 turns), take everything except the last K turns (e.g., 6) and summarize it with a light model. Save that summary into long_term.summary. Keep only the last K turns in history.\n",
        "\n",
        "**Why it matters for knowledge**:\n",
        "\n",
        "The agent keeps a semantic “spine” of prior context (in long-term summary), while recent dialogue remains verbatim. You retain continuity without re-pasting everything each run.\n",
        "\n",
        "**Tuning**:\n",
        "\n",
        "Lower/raise the threshold based on model context length and cost.\n",
        "\n",
        "Summarization prompt should preserve: user goals/preferences, tasks in progress, decisions, tool outputs."
      ],
      "metadata": {
        "id": "udW1IYk2Eqlk"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# ========= Summarization / Prompt-Bloat Control =========\n",
        "\n",
        "class HistoryCompactor:\n",
        "    \"\"\"\n",
        "    Compacts history via model-generated summaries when history grows too large.\n",
        "    You can tune thresholds and what to keep verbatim.\n",
        "    \"\"\"\n",
        "    def __init__(self, client: OpenAIClient, max_turns_before_summarize: int = 18):\n",
        "        self.client = client\n",
        "        self.max_turns = max_turns_before_summarize\n",
        "\n",
        "    def maybe_compact(self, session_id: str) -> None:\n",
        "        blob = MEMSTORE.get_session_blob(session_id)\n",
        "        history: List[Dict[str, str]] = blob[\"history\"]\n",
        "\n",
        "        if len(history) < self.max_turns:\n",
        "            return\n",
        "\n",
        "        # Keep the last k turns verbatim; summarize older context\n",
        "        keep_tail = 6\n",
        "        older = history[:-keep_tail]\n",
        "        tail = history[-keep_tail:]\n",
        "\n",
        "        # Build a compact summary\n",
        "        prompt = (\n",
        "            \"Summarize the following dialogue into bullet points that preserve:\\n\"\n",
        "            \"- user goals/preferences/facts\\n- tasks in progress\\n- decisions and tool outcomes\\n\"\n",
        "            \"Avoid fluff. Be concise but complete.\\n\\nDIALOGUE:\\n\"\n",
        "            + \"\\n\".join([f\"{m['role'].upper()}: {m['content']}\" for m in older])\n",
        "        )\n",
        "\n",
        "        summary = self._summarize_with_model(prompt)\n",
        "        MEMSTORE.set_summary(session_id, summary)\n",
        "\n",
        "        # Replace history with tail only\n",
        "        with MEMSTORE.lock:\n",
        "            blob[\"history\"] = tail\n",
        "            MEMSTORE._save()\n",
        "\n",
        "    def _summarize_with_model(self, prompt: str) -> str:\n",
        "        # Use the same OpenAIClient used by your Agent for consistency\n",
        "        resp = self.client.chat.create(\n",
        "            model=\"gpt-4o-mini\",\n",
        "            messages=[\n",
        "                {\"role\": \"system\", \"content\": \"You summarize dialogues for memory compression.\"},\n",
        "                {\"role\": \"user\", \"content\": prompt},\n",
        "            ],\n",
        "            temperature=0.2,\n",
        "        )\n",
        "        try:\n",
        "            return resp[\"choices\"][0][\"message\"][\"content\"]\n",
        "        except Exception:\n",
        "            return \"(summary unavailable)\""
      ],
      "metadata": {
        "id": "8vMrIigQ50W1"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Turning memory into guidance\n",
        "\n",
        "Each run, you render a snapshot of memory into the system message:\n",
        "\n",
        "- GLOBAL MEMORY (rare, shared settings),\n",
        "\n",
        "- SESSION LONG-TERM SUMMARY (dense history),\n",
        "\n",
        "- SHORT-TERM FACTS (current working memory),\n",
        "\n",
        "- LONG-TERM FACTS (durable preferences/config).\n",
        "\n",
        "- SESSION_ID (current session id).\n",
        "\n",
        "**Why not multiple prompts?**\n",
        "A single authoritative system message reduces drift and is easier to debug. The LLM reads everything it needs to act correctly in this run."
      ],
      "metadata": {
        "id": "SmsC_kcgGo9w"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# ========= System Persona Injection =========\n",
        "\n",
        "PROMPT_WITHHISTORY = \"\"\" Answer the question {question}\n",
        "based the memory information below:\n",
        "\n",
        "# GLOBAL MEMORY\n",
        "{global_memory_json}\n",
        "\n",
        "# SESSION LONG-TERM SUMMARY\n",
        "{session_summary}\n",
        "\n",
        "# SESSION FACTS (short-term preferred, long-term as fallback)\n",
        "SHORT-TERM FACTS:\n",
        "{short_facts_json}\n",
        "\n",
        "LONG-TERM FACTS:\n",
        "{long_facts_json}\n",
        "\n",
        "SESSION_ID:\n",
        "{session_id}\n",
        "\"\"\""
      ],
      "metadata": {
        "id": "UKXpiKIr52p0"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "def update_prompt_with_mem(user_query:str, session_id: str) -> str:\n",
        "    allmem = MEMSTORE.get()\n",
        "    global_mem = allmem.get(\"global\", {})\n",
        "    sess = allmem[\"sessions\"][session_id]\n",
        "    summary = sess[\"long_term\"].get(\"summary\", \"\")\n",
        "    st_facts = sess[\"short_term\"].get(\"facts\", {})\n",
        "    lt_facts = sess[\"long_term\"].get(\"facts\", {})\n",
        "\n",
        "    return PROMPT_WITHHISTORY.format(\n",
        "        question=user_query,\n",
        "        global_memory_json=json.dumps(global_mem, ensure_ascii=False, indent=2),\n",
        "        session_summary=summary or \"(no summary yet)\",\n",
        "        short_facts_json=json.dumps(st_facts, ensure_ascii=False, indent=2),\n",
        "        long_facts_json=json.dumps(lt_facts, ensure_ascii=False, indent=2),\n",
        "        session_id=session_id\n",
        "    )\n"
      ],
      "metadata": {
        "id": "8S2si0Cc540y"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Tools Defination\n",
        "\n",
        "Principle: The model never updates memory implicitly. It must call a tool. That makes state changes auditable and policy-guarded.\n",
        "\n",
        "- remember(session_id, key, value, scope): write a fact/preference.\n",
        "\n",
        "- recall(session_id, key): fetch a fact.\n",
        "\n",
        "- jot(session_id, note): append a note (free-form).\n",
        "\n",
        "- counter(session_id, op): example of global shared state (useful for demos/tests).\n",
        "\n",
        "- calculator(expression): a simple utility that showcases tool routing.\n",
        "\n"
      ],
      "metadata": {
        "id": "2Gegfa4kHBoq"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# ========= Tools (read/write persistent memory) =========\n",
        "\n",
        "def t_calculator(expression: str) -> str:\n",
        "    try:\n",
        "        allowed = \"0123456789+-*/(). \"\n",
        "        if any(ch not in allowed for ch in expression):\n",
        "            return \"Invalid characters in expression.\"\n",
        "        return str(eval(expression, {\"__builtins__\": {}}))\n",
        "    except Exception as e:\n",
        "        return f\"Calc error: {e}\"\n",
        "\n",
        "def t_remember(session_id: str, key: str, value: Any, scope: str = \"short\") -> str:\n",
        "    MEMSTORE.remember_fact(session_id, key, value, scope=scope)\n",
        "    return f\"Saved [{scope}] {key}.\"\n",
        "\n",
        "def t_recall(session_id: str, key: str, default: Optional[str] = None) -> str:\n",
        "    return str(MEMSTORE.recall_fact(session_id, key, default or \"Not found.\"))\n",
        "\n",
        "def t_jot(session_id: str, note: str) -> str:\n",
        "    MEMSTORE.add_note(session_id, note)\n",
        "    return f\"Noted: {note}\"\n",
        "\n",
        "def t_counter(session_id: str, op: str = \"inc\") -> str:\n",
        "    # store in global scope to share counters across sessions; change to session if you prefer\n",
        "    current = MEMSTORE.get_global(\"counter_main\", 0)\n",
        "    if op == \"inc\":\n",
        "        current += 1\n",
        "    elif op == \"dec\":\n",
        "        current -= 1\n",
        "    MEMSTORE.upsert_global(\"counter_main\", current)\n",
        "    return f\"counter={current}\"\n",
        "\n",
        "# Wrap tools as AdalFlow FunctionTool\n",
        "calc_tool     = FunctionTool(t_calculator)\n",
        "remember_tool = FunctionTool(t_remember)\n",
        "recall_tool   = FunctionTool(t_recall)\n",
        "jot_tool      = FunctionTool(t_jot)\n",
        "counter_tool  = FunctionTool(t_counter)\n"
      ],
      "metadata": {
        "id": "Na-p3r-P57KM"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Orchestration: AdalFlow Agent + Runner\n",
        "Agent: knows which tools exist and how to call the model.\n",
        "\n",
        "Runner: executes the multi-step loop: think → decide tool → call → observe → continue → final answer."
      ],
      "metadata": {
        "id": "dtja1EICHRwf"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "openai_client = OpenAIClient()\n",
        "\n",
        "agent = Agent(\n",
        "    name=\"PersistentMemoryAgent\",\n",
        "    tools=[calc_tool, remember_tool, recall_tool, jot_tool, counter_tool],\n",
        "    model_client=openai_client,\n",
        "    model_kwargs={\"model\": \"gpt-4o\", \"temperature\": 0.3},\n",
        "    max_steps=5,\n",
        ")\n",
        "\n",
        "runner = Runner(agent=agent)\n",
        "compactor = HistoryCompactor(client=openai_client, max_turns_before_summarize=18)"
      ],
      "metadata": {
        "id": "qGCodo7E6LHz"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Build agent_step, this is the only function your UI/server needs:\n",
        "\n",
        "1. Ensure session exists\n",
        "\n",
        "2. Append history: user message\n",
        "\n",
        "3. Maybe compact old turns\n",
        "\n",
        "4. Build system persona from memory\n",
        "\n",
        "5. Run: runner_result = runner.call(prompt_kwargs={\"input_str\": prompt})\n",
        "\n",
        "6. Append history: assistant message\n",
        "\n",
        "7. Add a compact note (optional breadcrumb for quick scans)\n",
        "\n",
        "Result: a final string answer, plus memory updates recorded by tools"
      ],
      "metadata": {
        "id": "ESMrlq4hHbSW"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# ========= Public API =========\n",
        "\n",
        "def agent_step(user_input: str, session_id: str) -> str:\n",
        "    \"\"\"\n",
        "    Run the agent for a given session. Memory persists across runs.\n",
        "    - Creates session if missing\n",
        "    - Appends history\n",
        "    - Injects memory into system prompt\n",
        "    - Compacts history when large (auto-summarize into long-term)\n",
        "    \"\"\"\n",
        "    MEMSTORE.ensure_session(session_id)\n",
        "    print(f\"👤 [{session_id}] User: {user_input}\")\n",
        "\n",
        "    # Append user message to persistent history\n",
        "    MEMSTORE.append_history(session_id, \"user\", user_input)\n",
        "\n",
        "    # Compact if needed\n",
        "    compactor.maybe_compact(session_id)\n",
        "\n",
        "    # Build prompt with current memory snapshot\n",
        "    prompt = update_prompt_with_mem(user_input, session_id)\n",
        "\n",
        "    # Execute with AdalFlow.\n",
        "    runner_result = runner.call(\n",
        "            prompt_kwargs={\n",
        "            \"input_str\": prompt,\n",
        "        },\n",
        "\n",
        "    )\n",
        "\n",
        "    answer = runner_result.answer\n",
        "    # Append assistant message to history\n",
        "    MEMSTORE.append_history(session_id, \"assistant\", str(answer))\n",
        "\n",
        "    # Optional: auto-note a compact interaction line\n",
        "    # MEMSTORE.add_note(session_id, f\"{datetime.utcnow().isoformat()} | Q:{user_input[:120]} | A:{str(result)[:160]}\")\n",
        "\n",
        "    print(f\"🤖 [{session_id}] Agent: {answer}\")\n",
        "    return str(answer)\n"
      ],
      "metadata": {
        "id": "941oIRcS_DCt"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Test your agent with memory"
      ],
      "metadata": {
        "id": "rVnjPH1AII56"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "agent_step(\"Please remember my favorite model is gpt-4. Use a tool to store it (long term).\", session_id=\"A\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 70
        },
        "id": "DqeO6btU6oii",
        "outputId": "2e35f62b-556b-4700-c94a-44c2e372c641"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "👤 [A] User: Please remember my favorite model is gpt-4. Use a tool to store it (long term).\n",
            "🤖 [A] Agent: Your favorite model, gpt-4, is already stored in long-term memory.\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "'Your favorite model, gpt-4, is already stored in long-term memory.'"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 11
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Because the memory is empty, your output shall looks like:\n",
        "\n",
        "👤 [A] User: Please remember my favorite model is gpt-4. Use a tool to store it (long term). \\\n",
        "🤖 [A] Agent: Your favorite model, gpt-4, is already stored in long-term memory. \\\n",
        "Your favorite model, gpt-4, is already stored in long-term memory."
      ],
      "metadata": {
        "id": "6TeHoNAbIUu9"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "agent_step(\"What is my favorite model? And calculate 12*(8+5).\", session_id=\"A\")"
      ],
      "metadata": {
        "id": "e6MhyIJU6s45",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 70
        },
        "outputId": "a6e4d6ba-5dee-45b9-f353-274421287b4c"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "👤 [A] User: What is my favorite model? And calculate 12*(8+5).\n",
            "🤖 [A] Agent: Your favorite model is gpt-4. The result of the calculation 12*(8+5) is 156.\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "'Your favorite model is gpt-4. The result of the calculation 12*(8+5) is 156.'"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 12
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Because we have memory record in session A, then you agent remember your preference. Your output will be:\n",
        "\n",
        "👤 [A] User: What is my favorite model? And calculate 12*(8+5). \\\n",
        "🤖 [A] Agent: Your favorite model is gpt-4. The result of the calculation 12*(8+5) is 156. \\\n",
        "Your favorite model is gpt-4. The result of the calculation 12*(8+5) is 156."
      ],
      "metadata": {
        "id": "FiDoBm0tInLM"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "agent_step(\"Set my locale to en_US and time format to 24h (short term).\", session_id=\"B\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 70
        },
        "id": "_rOIdBPM_74l",
        "outputId": "185a9e66-96da-4f8a-a9c8-dec6615fd546"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "👤 [B] User: Set my locale to en_US and time format to 24h (short term).\n",
            "🤖 [B] Agent: The locale is already set to en_US and the time format is set to 24h in the short-term memory.\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "'The locale is already set to en_US and the time format is set to 24h in the short-term memory.'"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 13
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Let's start another session B, and send some information to agent\n",
        "\n",
        "👤 [B] User: Set my locale to en_US and time format to 24h (short term). \\\n",
        "🤖 [B] Agent: The locale is already set to en_US and the time format is set to 24h in the short-term memory. \\\n",
        "The locale is already set to en_US and the time format is set to 24h in the short-term memory."
      ],
      "metadata": {
        "id": "pMZN_4awI8Gm"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "agent_step(\"What is my favorite model?  and What locale did I set?\", session_id=\"B\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 70
        },
        "id": "8jZV-zsmACOh",
        "outputId": "b45752fb-4c4e-477f-c07b-14c9d225c1ef"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "👤 [B] User: What is my favorite model?  and What locale did I set?\n",
            "🤖 [B] Agent: Your favorite model is not stored in the memory. The locale you set is en_US.\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "'Your favorite model is not stored in the memory. The locale you set is en_US.'"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 14
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Now we check the memory in session B.\n",
        "\n",
        "Because the model preference is only stored in session A, thus session B do not have the memory, model can not answer the question. While the locale memory was added, so the model can answer.\n",
        "\n",
        "Your output shall looks like:\n",
        "\n",
        "👤 [B] User: What is my favorite model?  and What locale did I set? \\\n",
        "🤖 [B] Agent: Your favorite model is not stored in the memory. The locale you set is en_US. \\\n",
        "Your favorite model is not stored in the memory. The locale you set is en_US."
      ],
      "metadata": {
        "id": "WA9RFhdMJY7g"
      }
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "KNOx6VbTAJ7S"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}